#pragma once
/*
    Requestor:客户端管理请求;
    一个map:保存 请求id和请求描述字段 的映射关系
    一个结构体:对请求各个字段进行描述
    两个send接口:异步发送请求,回调发送请求
    一个函数:找到响应对应的请求id,对Dispatcher模块提供响应处理函数(onResponse)
*/
#include "../common/net.hpp"
#include "../common/message.hpp"
#include <future>

namespace tmh_rpc
{
    namespace client
    {
        class Requestor
        {
        public:
            using ptr = std::shared_ptr<Requestor>;
            using RequestCallback = std::function<void(const BaseMessage::ptr &)>;
            using AsyncResponse = std::future<BaseMessage::ptr>;

            // 对于请求的描述
            struct RequestorDescribe
            {
                using ptr = std::shared_ptr<RequestorDescribe>;
                BaseMessage::ptr request;
                RType rtype;
                // 异步请求的调用结果
                std::promise<BaseMessage::ptr> response;
                // 回调函数
                RequestCallback callback;
            };

            // 给Dispatcher模块提供的回调处理函数:客户端会收到响应,将响应和请求描述对应起来
            void onResponse(const BaseConnection::ptr &conn, BaseMessage::ptr &msg)
            {
                string rid = msg->rid();
                RequestorDescribe::ptr rdp = getDescribe(rid);
                if (rdp.get() == nullptr)
                {
                    ELOG("收到响应-%s,但是未找到对应的请求描述!", rid.c_str());
                    return;
                }
                // 找到了响应对应的请求,并且做出不同的处理
                if (rdp->rtype == RType::REQ_ASYNC)
                {
                    // 将异步调用的结果保存起来
                    rdp->response.set_value(msg);
                }
                else if (rdp->rtype == RType::REQ_CALLBACK)
                {
                    if (rdp->callback)
                        rdp->callback(msg);
                }
                else
                {
                    ELOG("请求类型未知!!");
                }
                delDescribe(rid);
            }

            // 异步调用
            bool send(const BaseConnection::ptr &conn, const BaseMessage::ptr &req, AsyncResponse &async_rsp)
            {
                RequestorDescribe::ptr rdp = newDescribe(req, RType::REQ_ASYNC);
                // 差错检测
                if (rdp.get() == nullptr)
                {
                    ELOG("构造请求描述对象失败!");
                    return false;
                }
                // 发送数据
                conn->send(req);
                // 获取关联的future对象
                async_rsp = rdp->response.get_future();
                return true;
            }

            // 通过异步调用的接口实现同步调用
            bool send(const BaseConnection::ptr &conn, const BaseMessage::ptr &req, BaseMessage::ptr &rsp)
            {
                AsyncResponse rsp_future;
                bool ret = send(conn, req, rsp_future);
                if (ret == false)
                    return false;

                rsp = rsp_future.get();
                return true;
            }

            // 回调
            bool send(const BaseConnection::ptr &conn, const BaseMessage::ptr &req, RequestCallback &cb)
            {
                RequestorDescribe::ptr rdp = newDescribe(req, RType::REQ_CALLBACK, cb);
                // 差错检测
                if (rdp.get() == nullptr)
                {
                    ELOG("构造请求描述对象失败!");
                    return false;
                }
                // 发送数据
                conn->send(req);
                return true;
            }

        private:
            // 对_request_desc管理的接口(增删查改)
            RequestorDescribe::ptr newDescribe(const BaseMessage::ptr &req, RType rtype,
                                               const RequestCallback &cb = RequestCallback())
            {
                //代码出错的地方
                std::unique_lock<std::mutex> lock(_mutex);

                // 构造请求描述,将请求id的请求描述对应并且进行管理
                RequestorDescribe::ptr rd = std::make_shared<RequestorDescribe>();

                rd->request = req;
                rd->rtype = rtype;

                // 在send函数中:发送请求之前需要newRequestorDesc
                // send回调则传入callback函数,send异步调用则不传callback
                if (rtype == RType::REQ_CALLBACK && cb)
                {
                    rd->callback = cb;
                }
                _request_desc.insert({req->rid(), rd});
                return rd;
            }
            
            RequestorDescribe::ptr getDescribe(const std::string &rid)
            {
                std::unique_lock<std::mutex> lock(_mutex);
                auto it = _request_desc.find(rid);
                if (it == _request_desc.end())
                {
                    return RequestorDescribe::ptr();
                }
                return it->second;
            }
            void delDescribe(const std::string &rid)
            {
                std::unique_lock<std::mutex> lock(_mutex);
                _request_desc.erase(rid);
            }

        private:
            std::mutex _mutex;
            std::unordered_map<std::string, RequestorDescribe::ptr> _request_desc;
        };
    }
}
